#pragma once

#include <iostream>
#include <string>
#include <vector>

namespace composite {
    
/// @brief 抽象构件, 为树叶构件和树枝构件声明公共接口，并实现它们的默认行为
///        - 透明式: 声明访问和管理子类的接口
///        - 安全式: 不声明访问和管理子类的接口，管理工作由树枝构件完成
class AbstractTransparentComponent {
public:
    virtual ~AbstractTransparentComponent() {}

    // 透明式下, 需声明所有子类中的全部方法
    virtual void Add(AbstractTransparentComponent* comp) = 0;
    virtual void Remove(AbstractTransparentComponent* comp) = 0;
    virtual AbstractTransparentComponent* GetChild(const int id) = 0;

    virtual void Operation() = 0;
};

/// @brief 树叶构件, 组合中的叶节点对象，它没有子节点，用于实现抽象构件角色中声明的公共接口
class LeafTransparentComponent : public AbstractTransparentComponent {
public:
    LeafTransparentComponent(const std::string& name) : name_(name) {}

    // 透明式下, 树叶构件需要实现的不必要接口 (可空实现或跑出异常)
    virtual void Add(AbstractTransparentComponent* comp) {
        printf("Error! Leaf can't realize Add!\n");
    }
    virtual void Remove(AbstractTransparentComponent* comp) {
        printf("Error! Leaf can't realize Remove!\n");
    }
    virtual AbstractTransparentComponent* GetChild(const int id) {
        printf("Error! Leaf can't realize GetChild!\n");
        return nullptr;
    }

    // 树叶构件切实需要实现的接口
    virtual void Operation() {
        printf("this(%s) is LeafTransparentComponent::Operation!\n", name_.c_str());
    }

private:
    std::string name_;
};

/// @brief 树枝构件, 组合中的分支节点对象，它有子节点。它实现了抽象构件角色中声明的接口，主要作用是存储和管理子部件
class CompositeTransparentComponent : public AbstractTransparentComponent {
public:
    virtual void Add(AbstractTransparentComponent* comp) {
        children_.emplace_back(comp);
    }
    virtual void Remove(AbstractTransparentComponent* comp) {
        for (auto it = children_.begin(); it != children_.end(); it++) {
            if (comp == (*it)) {
                children_.erase(it++);
                break;
            }
        }
    }
    virtual AbstractTransparentComponent* GetChild(const int id) {
        if (id >= 0 && id < children_.size()) return children_[id];
        return nullptr;
    }
    virtual void Operation() {
        for (auto* child : children_) {
            child->Operation();
        }
    }

private:
    std::vector<AbstractTransparentComponent*> children_;
};

} // namespace composite

void Test_12_composite_impl_1() {
    printf("-------------------- %s --------------------\n", __FUNCTION__);
    std::shared_ptr<composite::AbstractTransparentComponent> composite_ptr(new composite::CompositeTransparentComponent());

    std::shared_ptr<composite::AbstractTransparentComponent> leaf_ptr_A(new composite::LeafTransparentComponent("leaf A"));
    std::shared_ptr<composite::AbstractTransparentComponent> leaf_ptr_B(new composite::LeafTransparentComponent("leaf B"));
    std::shared_ptr<composite::AbstractTransparentComponent> leaf_ptr_C(new composite::LeafTransparentComponent("leaf C"));

    composite_ptr->Add(leaf_ptr_A.get());
    composite_ptr->Add(leaf_ptr_B.get());
    composite_ptr->Add(leaf_ptr_C.get());

    composite::AbstractTransparentComponent* tmp_ptr = composite_ptr->GetChild(1);
    tmp_ptr->Operation();
    composite_ptr->Remove(leaf_ptr_B.get());

    composite_ptr->Operation();
}

// -------------------- Test_12_composite_impl_1 --------------------
// this(leaf B) is LeafTransparentComponent::Operation!
// this(leaf A) is LeafTransparentComponent::Operation!
// this(leaf C) is LeafTransparentComponent::Operation!